package com.ywcq.netty.httpserver.helloworld;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


/**
 * 默认配置信息：
 * IP_TOS:0
 SO_RCVBUF:16384
 SO_LINGER:-1
 RCVBUF_ALLOCATOR: 	
	    static final int DEFAULT_MINIMUM = 64;
        static final int DEFAULT_INITIAL = 1024;
        static final int DEFAULT_MAXIMUM = 65536;
 SINGLE_EVENTEXECUTOR_PER_GROUP:true
 ALLOW_HALF_CLOSURE:false
 WRITE_SPIN_COUNT:16
 AUTO_CLOSE:true
 CONNECT_TIMEOUT_MILLIS:30000
 MESSAGE_SIZE_ESTIMATOR:8
 WRITE_BUFFER_LOW_WATER_MARK:32768
 WRITE_BUFFER_HIGH_WATER_MARK:65536
 SO_SNDBUF:8192
 SO_REUSEADDR:false
 TCP_NODELAY:true
 ALLOCATOR:PooledByteBufAllocator(directByDefault: true)
 SO_KEEPALIVE:false
 AUTO_READ:true
 MAX_MESSAGES_PER_READ:16
 * @author hdb
 *
 */
@Configuration
@ConfigurationProperties(prefix = "http-server")  
@Component("httpserverconfig")
public class Config 
{
	private Integer port = 8601;
	
	private Boolean isSsl = false;
	
	private Integer boosGroupThreads = 1;
	
	private Integer workerGroupThreads = 3;
	
	/**
	 * TCP 缓存(TCP Backlog) 通常情况下，操作系统会使用一块限定的内存来处理 TCP 连接请求。
	 * 每当用户端发送的 SYN 标志置位连接请求到服务端的一个合法端口(提供 TCP 服务的一端监听该端口)时，
	 * 处理所有连接请求的内存使用量必须进行限定。如果不进行限定，系统会因处理大量的 TCP 连接请求而耗尽内存，
	 * 这在某种程度上可以说是一种简单的 DoS 攻击。
	 * 这块经过限定的， 用于处理 TCP 连接的内存称为 TCP 缓存(TCP Backlog)，
	 * 它实际上是用于处理进站(inbound)连接请求的一个队列。
	 * 该队列保存那些处于半开放(half-open)状态的 TCP 连接项目，和已建立完整连接但仍未由应用程序通过 accept()调用提取的项目。
	 * 如果这个缓存队列被填满，除非可以及时处理队列中的项目，否则任何其它新的 TCP 连接请求会被丢弃。 
	 * 
	 * 一般情况下，该缓存队列的容量很小。原因很简单，在正常的情况下 TCP 可以很好的处 理连接请求。
	 * 如果当缓存队列填满的时候新的客户端连接请求被丢弃，客户端只需要简单的重新发送连接请求，
	 * 服务端有时间清空缓存队列以相应新的连接请求。在现实环境中，不同操作系统支持 TCP 缓冲队列有所不同。
	 * backlog 指定了内核为此套接口排队的最大连接个数，对于给定在监听套接口，内核要维护两个对列，
	 * 未链接队列和已连接队列，根据TCP三路握手过程中三个分节来分隔这两个队列。
	 * 服务器处于listen状态时收到客户端syn分节(connect)时在未完成队列中创建一个新在条目，
	 * 然后用三路握手的第二个分节几服务器在syn响应阴对客户端syn的ack，
	 * 此条目用第三个分节到达前（客户端对服务器syn在ack） 一直保留在未完成连接列队中如果三路握手完成，
	 * 该将从未完成队列搬到已完成队列尾部。当进程调用accept时，从已完成队列中在头部取出一个条目给进程，
	 * 当已完成队列为空时进程 将睡眠，直到有条目在已完成连接列队中才唤醒。
	 * backlog 被规定为两二个队列总和在最大值，大多数实现默认值为5,但在高并发web服务器中此值显然不够，
	 * lighttpd中此值达到128*8.
	 * 需要分清楚此值更大一些在原因是未完成连接发队列在长度可能因为客户端syn的到达及等待三路招手
	 * 第三个分节在到达延时而增大。netty 默认100
	 */
	private Integer socketOptionBacklog = 100;
	
	/**
	 * 保持连接检测对方主机是否崩溃，避免（服务器）永远阻塞于TCP连接的输入。
	 * 设置该选项后，如果2小时内在此套接口的任一方向都没有数据交换，
	 * TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。
	 */
	private Boolean socketOptionKeepalive = false;
	
	/**
	 * SO_BROADCAST此项是启动和禁用发送广播消息的处理能力，
	 * 它仅用于数据报套接字和支持广播消息概念的网络上。
	 * 默认情况为datagramSocket设置此选项
	 */
	private Boolean socketOptionBroadcast = false;
	
	/**
	 * 接收缓冲区缓冲区。
	 * 先明确一个概念：每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区，
	 * TCP的全双工的工作模式以及TCP的滑动窗口便是依赖于这两个独立的buffer以及此buffer的填充状态。
	 * 接收缓冲区把数据缓存入内核，应用进程一直没有调用read进行读取的话，此数据会一直缓存在相应socket的接收缓冲区内。
	 * 再啰嗦一点，不管进程是否读取socket，对端发来的数据都会经由内核接收并且缓存到socket的内核接收缓冲区之中。
	 * read所做的工作，就是把内核缓冲区中的数据拷贝到应用层用户的buffer里面，仅此而已。
	 * 进程调用send发送的数据的时候，最简单情况（也是一般情况），将数据拷贝进入socket的内核发送缓冲区之中，
	 * 然后send便会在上层返回。
	 * 换句话说，send返回之时，数据不一定会发送到对端去（和write写文件有点类似），send仅仅是把应用层buffer的数据拷贝进socket的内核发送buffer中。
	 * 每个UDP socket都有一个接收缓冲区，没有发送缓冲区，从概念上来说就是只要有数据就发，不管对方是否可以正确接收，
	 * 所以不缓冲，不需要发送缓冲区。
	 * 接收缓冲区被TCP和UDP用来缓存网络上来的数据，一直保存到应用进程读走为止。
	 * 对于TCP，如果应用进程一直没有读取，buffer满了之后，发生的动作是：通知对端TCP协议中的窗口关闭。
	 * 这个便是滑动窗口的实现。保证TCP套接口接收缓冲区不会溢出，从而保证了TCP是可靠传输。
	 * 因为对方不允许发出超过所通告窗口大小的数据。
	 * 这就是TCP的流量控制，如果对方无视窗口大小而发出了超过窗口大小的数据，则接收方TCP将丢弃它。 
	 * UDP：当套接口接收缓冲区满时，新来的数据报无法进入接收缓冲区，此数据报就被丢弃。
	 * UDP是没有流量控制的；快的发送者可以很容易地就淹没慢的接收者，导致接收方的UDP丢弃数据报。
	 * 以上便是TCP可靠，UDP不可靠的实现。
	 * 
	 */
	private Integer socketOptionRecieveBuffer = 1024*16;
	
	
	/**
	 * 发送缓冲区
	 * 
	 */
	private Integer socketOptionSendBuffer = 1024*16;
	
	/**
	 * SO_REUSEADDR允许一个程序在多个实例对同一个端口进行绑定。
	 * 如果你定义个SO_REUSEADDR，只定义一个套接字在一个端口上进行监听，
	 * 如果服务器出现意外而导致没有将这个端口释放，那么服务器重新启动后，你还可以用这个端口，
	 * 因为你已经规定可以重用了，如果你没定义的话，你就会得到提示，ADDR已在使用中。
	 * 用在多播的时候，也经常使用SO_REUSEADDR，也是为了防止机器出现意外，导致端口没有释放，而使重启后的绑定失败。
	 */
	private Boolean socketOptionReuseAddr = true;
	
	/**
	 * linger
	 * 此选项指定函数close对面向连接的协议如何操作（如TCP）。
	 * 内核缺省close操作是立即返回，如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
	 */
	private Integer socketOptionLinger = 16;
	
	/**
	 * SO_TIMEOUT
	 */
	private Integer socketOptionTimeOut = 16;
	
	/**
	 * 半关闭socket
	 * TCP和SCTP允许用户关闭一个socket的出站流量而不用完全关闭它。
	 * 这样的socket被称为“半关闭socket”，同时用户能够通过调用SocketChannel.shutdownOutput()方法来获取一个半关闭socket。
	 * 如果一个远端关闭了出站通道，SocketChannel.read(..)会返回-1，这看上去并没有和一个关闭了的链接有什么区别。
	 * Netty3.x没有shutdownOutput()操作。同样，它总是在SocketChannel.read(..)返回-1的时候关闭链接。
	 * 要支持半关闭socket，4.0增加了SocketChannel.shutdownOutput()方法，
	 * 同时用户能设置“ALLOW_HALF_CLOSURE”的ChanneOption来阻止Netty在SocketChannel.read(..)返回-1的时候自动关闭链接。
	 */
	private Boolean socketOptionAllowHalfClosure = false;
	
	public Boolean getSocketOptionAllowHalfClosure() {
		return socketOptionAllowHalfClosure;
	}

	public void setSocketOptionAllowHalfClosure(Boolean socketOptionAllowHalfClosure) {
		this.socketOptionAllowHalfClosure = socketOptionAllowHalfClosure;
	}

	public Integer getSocketOptionBacklog() {
		return socketOptionBacklog;
	}

	public void setSocketOptionBacklog(Integer socketOptionBacklog) {
		this.socketOptionBacklog = socketOptionBacklog;
	}

	public Boolean getSocketOptionKeepalive() {
		return socketOptionKeepalive;
	}

	public void setSocketOptionKeepalive(Boolean socketOptionKeepalive) {
		this.socketOptionKeepalive = socketOptionKeepalive;
	}

	public Boolean getSocketOptionBroadcast() {
		return socketOptionBroadcast;
	}

	public void setSocketOptionBroadcast(Boolean socketOptionBroadcast) {
		this.socketOptionBroadcast = socketOptionBroadcast;
	}

	public Integer getSocketOptionRecieveBuffer() {
		return socketOptionRecieveBuffer;
	}

	public void setSocketOptionRecieveBuffer(Integer socketOptionRecieveBuffer) {
		this.socketOptionRecieveBuffer = socketOptionRecieveBuffer;
	}

	public Integer getSocketOptionSendBuffer() {
		return socketOptionSendBuffer;
	}

	public void setSocketOptionSendBuffer(Integer socketOptionSendBuffer) {
		this.socketOptionSendBuffer = socketOptionSendBuffer;
	}

	public Boolean getSocketOptionReuseAddr() {
		return socketOptionReuseAddr;
	}

	public void setSocketOptionReuseAddr(Boolean socketOptionReuseAddr) {
		this.socketOptionReuseAddr = socketOptionReuseAddr;
	}

	public Integer getSocketOptionLinger() {
		return socketOptionLinger;
	}

	public void setSocketOptionLinger(Integer socketOptionLinger) {
		this.socketOptionLinger = socketOptionLinger;
	}

	public Integer getSocketOptionTimeOut() {
		return socketOptionTimeOut;
	}

	public void setSocketOptionTimeOut(Integer socketOptionTimeOut) {
		this.socketOptionTimeOut = socketOptionTimeOut;
	}

	public Integer getBoosGroupThreads() {
		return boosGroupThreads;
	}

	public void setBoosGroupThreads(Integer boosGroupThreads) {
		this.boosGroupThreads = boosGroupThreads;
	}

	public Integer getWorkerGroupThreads() {
		return workerGroupThreads;
	}

	public void setWorkerGroupThreads(Integer workerGroupThreads) {
		this.workerGroupThreads = workerGroupThreads;
	}

	public Integer getPort() {
		return port;
	}

	public void setPort(Integer port) {
		this.port = port;
	}


	public Boolean getIsSsl() {
		return isSsl;
	}

	public void setIsSsl(Boolean isSsl) {
		this.isSsl = isSsl;
	}
	
	@Override
	public String toString() {
		return "Config [port=" + port + ", isSsl=" + isSsl + ", boosGroupThreads="
				+ boosGroupThreads + ", workerGroupThreads=" + workerGroupThreads + "]";
	}

}
